package com.manlitech.cloudboot.config.dubboconfig;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.manlitech.cloudboot.common.constant.LogAndAuthConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.MDC;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author shensg
 * @Description dubbo拦截
 * @Date 2020:09:27 11:47
 */
@Activate(group = "consumer")
@Slf4j
public class DubboConsumerFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        /** request 透传*/
        requestTransport(invoker , invocation);
        /** 通过校验发送请求*/
        //接口方法名称
        String methodName = invocation.getMethodName();
        //请求地址的IP
        String remoteHost = RpcContext.getContext().getRemoteHost();
        //dubbo服务端IP
        String localHost = RpcContext.getContext().getLocalHost();
        //所有的参数
        Object[] params = invocation.getArguments();
        //接口名称
        Class<?> serviceType = invoker.getInterface();
        log.info("remoteHost=>{},localHost=>{},serviceType=>{},methodName=>{},params=>        {}",remoteHost,localHost,serviceType,methodName,params);

        //开始时间
        long startTime = System.currentTimeMillis();
        //调用结果
        Result result = invoker.invoke(invocation);
        //结束时间
        long elapsedTime = System.currentTimeMillis() - startTime;
        //如果发生错误
        if(result.hasException() && invoker.getInterface() != GenericService.class){
            log.error("远程提供dubbo服务执行异常=>{} " , result.getException());
        }else{
            log.info("远程提供的dubbo服务执行成功,服务:{} , 方法:{} , 耗时:{} " ,
                    invocation.getInvoker().getInterface().getName(),
                    invocation.getMethodName(),
                    elapsedTime);
        }
        return result;
    }
    /**
     * 参数透传
     */
    private void requestTransport(Invoker<?> invoker, Invocation invocation) {
        String token = null;
        String traceId = null;
        RpcContext context = RpcContext.getContext();
        /**
         * 获取 token 和 traceId
         */
        if (RpcContext.getContext().getRequest() != null && RpcContext.getContext().getRequest() instanceof HttpServletRequest) {
            /**
             * 注意的是只有在设置server="jetty"或server="tomcat"或server="servlet"才能通过以下的方式获取到。
             */
            //强转
            HttpServletRequest request = (HttpServletRequest) context.getRequest();
            //获取token
            token = request.getHeader(LogAndAuthConstant.TOKEN);
            //获取traceid
            traceId = request.getHeader(LogAndAuthConstant.TRACEID);
        }else{
            String attachmentMapStr = context.getAttachment("attachmentMapStr");
            if(attachmentMapStr!=null && !"".equals(attachmentMapStr)){
                Map<String,Object> attachmentMap = (Map) JSONObject.parse(attachmentMapStr);
                if(attachmentMap.size() > 0){
                    traceId = (String) attachmentMap.get(LogAndAuthConstant.TRACEID);
                    token = (String) attachmentMap.get(LogAndAuthConstant.TOKEN);
                }
            }
        }
        /**
         * 请求方可以没有token和tranceId , 但提供方比校验
         * 有可能在启动的时候查询链路是否可用
          */
        Map<String,Object> attachmentMap = new HashMap<>();
        attachmentMap.put(LogAndAuthConstant.TOKEN,token);
        attachmentMap.put(LogAndAuthConstant.TRACEID,traceId);
        String attachmentMapStr = JSON.toJSONString(attachmentMap);
        RpcContext.getContext()
                .setInvoker(invoker)
                .setInvocation(invocation)
                .setAttachment("attachmentMapStr",attachmentMapStr);
        //设置traceid
        MDC.put(LogAndAuthConstant.TRACEID , traceId);
    }

}
